/*
 * Copyright 2000 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "WtUpload.h"
#include "WtUtil.h"
#include "WtTable.h"
#include "WtTableUtil.h"
#include "WtMultiTable.h"
#include "WtClientRequest.h"

/* uploadSettings command */

void WtUploadSettingsUsage(Tcl_Interp *interp, Tcl_Obj *cmd)
{
  char *cmdStr = WtToString(cmd);
  Tcl_AppendResult(interp, wtBadUsagePrefix, "\n",
    cmdStr, " postMax ?size?\n",
    cmdStr, " allowUploads ?flag?\n",
    cmdStr, " tempDir ?path?\n",
    cmdStr, " uploadHandler ?script?\n",
    cmdStr, " uploadHandlerData ?data?\n",
    cmdStr, " info\n",
    NULL);
}

int WtUploadSettingsCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR, intVal;
  Tcl_Obj *interpRes = Tcl_GetObjResult(interp), *tbl;
  const char *subCmd;
  char *str;
  WtContext *w;

  w = (WtContext *)Tcl_GetAssocData(interp, "wt::context", NULL);

  if (objc < 2) {
    WtUploadSettingsUsage(interp, objv[0]);
    return TCL_ERROR;
  }

  subCmd = WtToString(objv[1]);

  if (!strcmp(subCmd, "postMax")) {

    /* postMax command */

    if (objc != 2 && objc != 3) {
      Tcl_AppendStringsToObj(interpRes, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " ?size?");
    } else if (objc == 2) {
      Tcl_SetObjResult(interp,
        Tcl_NewIntObj(w->web->clientRequest.apWeb->post_max));
      ret = TCL_OK;
    } else {
      if (Tcl_GetIntFromObj(interp, objv[2], &intVal) == TCL_OK) {
        ApacheRequest_set_post_max(w->web->clientRequest.apWeb, intVal);
        Tcl_SetObjResult(interp,
          Tcl_NewIntObj(w->web->clientRequest.apWeb->post_max));
        ret = TCL_OK;        
      }
    }

  } else if (!strcmp(subCmd, "allowUploads")) {

    /* allowUploads command */

    if (objc != 2 && objc != 3) {
      Tcl_AppendStringsToObj(interpRes, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " ?flag?");
    } else if (objc == 2) {
      Tcl_SetObjResult(interp,
        Tcl_NewBooleanObj(!w->web->clientRequest.apWeb->disable_uploads));
      ret = TCL_OK;
    } else {
      if (Tcl_GetBooleanFromObj(interp, objv[2], &intVal) == TCL_OK) {
        w->web->clientRequest.apWeb->disable_uploads = !intVal;
        Tcl_SetObjResult(interp,
          Tcl_NewBooleanObj(!w->web->clientRequest.apWeb->disable_uploads));
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "tempDir")) {

    /* tempDir command */

    if (objc != 2) {
      Tcl_AppendStringsToObj(interpRes, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " ?path?");
    } else if (objc == 2) {
      Tcl_SetObjResult(interp, WtNewString(w->web->clientRequest.apWeb->temp_dir));
      ret = TCL_OK;
    } else {
      str = WtToString(objv[2]);
      ApacheRequest_set_temp_dir(w->web->clientRequest.apWeb,
        ap_pstrdup(w->web->apReq->pool, str));
      Tcl_SetObjResult(interp, WtNewString(w->web->clientRequest.apWeb->temp_dir));
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "uploadHandler")) {

    /* uploadHandler command */

    if (objc != 2) {
      Tcl_AppendStringsToObj(interpRes, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " ?command?");
    } else if (objc == 2) {
      Tcl_SetObjResult(interp, w->web->clientRequest.uploadHandler);
      ret = TCL_OK;
    } else {
      w->web->clientRequest.uploadHandler = objv[2];
      Tcl_IncrRefCount(w->web->clientRequest.uploadHandler);
      Tcl_SetObjResult(interp, w->web->clientRequest.uploadHandler);
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "uploadHandlerData")) {

    /* uploadHandlerData command */

    if (objc != 2) {
      Tcl_AppendStringsToObj(interpRes, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " ?command?");
    } else if (objc == 2) {
      Tcl_SetObjResult(interp,
        w->web->clientRequest.uploadHandlerData ?
        w->web->clientRequest.uploadHandlerData : WtNewString(""));
      ret = TCL_OK;
    } else {
      if (w->web->clientRequest.uploadHandlerData) {
        Tcl_DecrRefCount(w->web->clientRequest.uploadHandlerData);
      }
      w->web->clientRequest.uploadHandlerData = objv[2];
      Tcl_IncrRefCount(w->web->clientRequest.uploadHandlerData);
      Tcl_SetObjResult(interp, w->web->clientRequest.uploadHandlerData);
      ret = TCL_OK;
    }

  } else if (!strcmp(subCmd, "info")) {

    /* info command - Create a table with the settings */

    tbl = WtNewTableObj();

    WtTableSetStrToInt(tbl, "postMax", w->web->clientRequest.apWeb->post_max);
    WtTableSetStrToBool(tbl, "allowUploads",
      !w->web->clientRequest.apWeb->disable_uploads);
    WtTableSetStrToStr(tbl, "tempDir",
      WtSafeStr(w->web->clientRequest.apWeb->temp_dir));
    WtTableSetStrToObj(tbl, "uploadHandler",
      w->web->clientRequest.uploadHandler);
    WtTableSetStrToObj(tbl, "uploadHandlerData",
      w->web->clientRequest.uploadHandlerData ?
      w->web->clientRequest.uploadHandlerData : WtNewString(""));

    Tcl_SetObjResult(interp, tbl);

    ret = TCL_OK;

  } else {

    /* unknown command */

    WtUploadSettingsUsage(interp, objv[0]);

  }

  return ret;
}

/* Create a new upload object */

Tcl_Obj *WtNewUploadObj(ApacheUpload *item, Tcl_Interp *interp)
{
  Tcl_Obj *obj = WtNewTableObj();

  WtTableSetStrToStr(obj, "name", WtSafeStr(item->name));
  WtTableSetStrToStr(obj, "fileName", WtSafeStr(item->filename));
  WtTableSetStrToStr(obj, "path", WtSafeStr(item->tempname));
  WtTableSetStrToStr(obj, "contentType",
    WtSafeStr(ApacheUpload_type(item)));
  WtTableSetStrToInt(obj, "size", ApacheUpload_size(item));

  return obj;
}

Tcl_Obj *WtUploadGetProperty(Tcl_Obj *upload, const char *key,
    Tcl_Interp *interp)
{
  Tcl_Obj *val = WtTableGetObjFromStr(upload, key, NULL);
  if (!val) {
    Tcl_AppendResult(interp, "Unable to read ", key,
      " property of upload object.");
    return NULL;
  }
  return val;
}

int WtUploadSetProperty(Tcl_Obj *upload, const char *key,
    Tcl_Obj *val, Tcl_Interp *interp)
{
  WtTableSetStrToObj(upload, key, val);
  return 1;
}

Tcl_Obj *WtUploadGetName(Tcl_Obj *upload, Tcl_Interp *interp)
{
  return WtUploadGetProperty(upload, "name", interp);
}

int WtUploadSetName(Tcl_Obj *upload, Tcl_Obj *val, Tcl_Interp *interp)
{
  return WtUploadSetProperty(upload, "name", val, interp);
}

Tcl_Obj *WtUploadGetFileName(Tcl_Obj *upload, Tcl_Interp *interp)
{
  return WtUploadGetProperty(upload, "fileName", interp);
}

int WtUploadSetFileName(Tcl_Obj *upload, Tcl_Obj *val, Tcl_Interp *interp)
{
  return WtUploadSetProperty(upload, "fileName", val, interp);
}

Tcl_Obj *WtUploadGetPath(Tcl_Obj *upload, Tcl_Interp *interp)
{
  return WtUploadGetProperty(upload, "path", interp);
}

int WtUploadSetPath(Tcl_Obj *upload, Tcl_Obj *val, Tcl_Interp *interp)
{
  return WtUploadSetProperty(upload, "path", val, interp);
}

Tcl_Obj *WtUploadGetContentType(Tcl_Obj *upload, Tcl_Interp *interp)
{
  return WtUploadGetProperty(upload, "contentType", interp);
}

int WtUploadSetContentType(Tcl_Obj *upload, Tcl_Obj *val, Tcl_Interp *interp)
{
  return WtUploadSetProperty(upload, "contentType", val, interp);
}

Tcl_Obj *WtUploadGetSize(Tcl_Obj *upload, Tcl_Interp *interp)
{
  return WtUploadGetProperty(upload, "size", interp);
}

int WtUploadSetSize(Tcl_Obj *upload, Tcl_Obj *val, Tcl_Interp *interp)
{
  return WtUploadSetProperty(upload, "size", val, interp);
}

Tcl_Channel WtCreateUploadChannel(ApacheUpload *item, Tcl_Interp *interp)
{
  FILE *uploadFile;
  Tcl_Channel uploadChan = NULL;

  if (uploadFile = ApacheUpload_FILE(item)) {
#ifdef WIN32
    uploadChan = Tcl_MakeFileChannel((ClientData)_get_osfhandle(
      _fileno(uploadFile)), TCL_READABLE);
#else
    uploadChan = Tcl_MakeFileChannel((ClientData)_fileno(
      uploadFile), TCL_READABLE);
#endif

    if (!uploadChan) {
      Tcl_AppendResult(interp, "Failed to create file channel.",
        NULL);
    } else {
      Tcl_RegisterChannel(interp, uploadChan);
    }
  }

  return uploadChan;
}

/* uploads command */

int WtUploadsCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  int ret;
  WtContext *w = WtGetAssocContext(interp);

  if (!WtReadRequest(w, interp)) {
    return TCL_ERROR;
  }

  WtMultiTableMethodHelper(clientData, interp, objc, objv, &ret,
    &w->web->clientRequest.uploads, &WtTableType, 0, 1);

  return ret;
}

int WtUploadCmd(ClientData clientData, Tcl_Interp *interp, int objc,
    Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  const char *subCmd;
  WtContext *w;
  Tcl_Obj *upload, *obj;

  w = (WtContext *)Tcl_GetAssocData(interp, "wt::context", NULL);

  if (objc < 2) {
    WtUploadUsage(interp, objv[0]);
    return TCL_ERROR;
  }

  subCmd = WtToString(objv[1]);

  /* Read the POST data if needed */

  if (!WtReadRequest(w, interp)) {
    return TCL_ERROR;
  }

  if (!strcmp(subCmd, "name")) {

    /* name command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " uploadVal");
    } else if (upload = WtUploadCmdGetVal(objv[2], interp)) {
      if (obj = WtUploadGetName(upload, interp)) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "fileName")) {

    /* fileName command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " uploadVal");
    } else if (upload = WtUploadCmdGetVal(objv[2], interp)) {
      if (obj = WtUploadGetFileName(upload, interp)) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "path")) {

    /* path command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " uploadVal");
    } else if (upload = WtUploadCmdGetVal(objv[2], interp)) {
      if (obj = WtUploadGetPath(upload, interp)) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "contentType")) {

    /* contentType command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " upload");
    } else if (upload = WtUploadCmdGetVal(objv[2], interp)) {
      if (obj = WtUploadGetContentType(upload, interp)) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      }
    }

  } else if (!strcmp(subCmd, "size")) {

    /* size command */

    if (objc != 3) {
      Tcl_AppendResult(interp, wtBadUsagePrefix,
        WtToString(objv[0]), " ", WtToString(objv[1]), " upload");
    } else if (upload = WtUploadCmdGetVal(objv[2], interp)) {
      if (obj = WtUploadGetSize(upload, interp)) {
        Tcl_SetObjResult(interp, obj);
        ret = TCL_OK;
      }
    }

  } else {

    /* Unknown command */

    WtUploadUsage(interp, objv[0]);

  }

  return ret;
}

void WtUploadUsage(Tcl_Interp *interp, Tcl_Obj *cmd)
{
  char *cmdStr = WtToString(cmd);
  Tcl_AppendResult(interp, wtBadUsagePrefix, "\n",
    cmdStr, " name upload\n",
    cmdStr, " fileName upload\n",
    cmdStr, " path upload\n",
    cmdStr, " contentType upload\n",
    cmdStr, " size upload\n",
    NULL);
}

void WtInitUploadCommands(Tcl_Interp *interp)
{
  WtContext *w = WtGetAssocContext(interp);

  Tcl_CreateObjCommand(interp, "::wt::request::uploadSettings",
    WtUploadSettingsCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::request::uploads",
    WtUploadsCmd, NULL, NULL);

  Tcl_CreateObjCommand(interp, "::wt::request::upload",
    WtUploadCmd, NULL, NULL);
}

Tcl_Obj *WtUploadCmdGetVal(Tcl_Obj *val, Tcl_Interp *interp)
{
  return WtCmdGetVal(val, &WtTableType, interp);
}
